diff --git a/decomposition-and-conditionals-homework/.gitignore b/decomposition-and-conditionals-homework/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..9dbe7d11c1294e17b42776f8336b4394281d223c --- /dev/null +++ b/decomposition-and-conditionals-homework/.gitignore @@ -0,0 +1,54 @@ +# Mac file finder metadata +.DS_Store +# Windows file metadata +._* +# Thumbnail image caches +Thumbs.db +ethumbs.db +# MS Office temporary file +~* +# Emacs backup file +*~ + +# Common +[Bb]in/ +[Bb]uild/ +[Oo]bj/ +[Oo]ut/ +[Tt]mp/ +[Xx]86/ +[Ii][Aa]32/ +[Xx]64/ +[Xx]86_64/ +[Xx]86-64/ +[Aa]rm +[Aa]32 +[Tt]32 +[Aa]64 +*.tmp +*.bak +*.bk +*.swp + +# Java files +*.class +javadoc/ + +# Maven +target/ + +# JetBrains (IntelliJ IDEA, PyCharm, etc) files +.idea/ +cmake-build-*/ +*.iml +*.iws +*.ipr + +# Eclipse files +.settings/ +.project +.classpath +.buildpath +.loadpath +.factorypath +local.properties diff --git a/decomposition-and-conditionals-homework/README.md b/decomposition-and-conditionals-homework/README.md new file mode 100644 index 0000000000000000000000000000000000000000..81e10a03547da4be7f691a0726b99527562889fd --- /dev/null +++ b/decomposition-and-conditionals-homework/README.md @@ -0,0 +1,57 @@ +# Decomposition and Conditionals Homework + +**Collaboration Policy**: No collaboration is permitted on this assignment. + +**Notes and References**: This is an open-book, open-note assignment. + +## Overview + +Proper problem and solution decomposition is an important software engineering +technique for writing readable and maintainable code. In this assignment you +will practice decomposition to solve two small computational problems whose +solutions may not be obvious from their descriptions. You will also practice +writing code that uses an enumerated type, string concatenation, and exception +handlers. + +## Learning Goals + +By completing this assignment, you should be able to: + +1. Decompose a solution to a problem into multiple methods in a Java program, + +2. Use an enumerated type to represent values from a limited space, + +3. Use string concatenation to format text output, and + +4. Use exception handlers to catch and handle input errors. + +## Instructions + +This assignment is to be completed individually; **no collaboration is +permitted**. + +### Clone the Starter Code and Set Up Eclipse + +1. In your virtual machine, create a new folder in your `soft160-homework` + folder named `homework5`. + +2. Change into the `homework5` directory. + +3. Clone + [the starter code](https://git.unl.edu/soft-core/soft-160/decomposition-and-conditionals-homework). + +4. Change into the `decomposition-and-conditionals-homework` directory. Verify that you are in + the correct directory by running the command `pwd`. + +5. Remove the Git folder that links this code to the instructors' repository by + running the command `chmod -R u+w .git` (which gives you permission to + remove the folder without extra confirmation prompts) and then the command + `rm -rI .git`. Answer `yes` when asked to confirm the deletion. + +6. Verify the Git folder has been removed by running `ls -a` and making sure + that you see folders with names like `src` and `documentation`, but that `.git` + is **not** one of the items listed. + +7. Start the Eclipse IDE. + +8. Import the starter code into Eclipse as a Maven project. diff --git a/decomposition-and-conditionals-homework/documentation/.gitkeep b/decomposition-and-conditionals-homework/documentation/.gitkeep new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/decomposition-and-conditionals-homework/pom.xml b/decomposition-and-conditionals-homework/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..fd501b2c7216b387114834beb2899eaccd72fa2a --- /dev/null +++ b/decomposition-and-conditionals-homework/pom.xml @@ -0,0 +1,36 @@ +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + <groupId>edu.unl.cse.soft160.decomposition_and_conditionals</groupId> + <artifactId>decomposition_and_conditionals</artifactId> + <packaging>jar</packaging> + <version>1.0-SNAPSHOT</version> + <name>decomposition_and_conditionals</name> + <url>http://maven.apache.org</url> + + <properties> + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> + </properties> + + <build> + <plugins> + <plugin> + <artifactId>maven-compiler-plugin</artifactId> + <version>3.8.1</version> + <configuration> + <source>8</source> + <target>8</target> + </configuration> + </plugin> + </plugins> + </build> + + <dependencies> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <version>4.13</version> + <scope>test</scope> + </dependency> + </dependencies> +</project> diff --git a/decomposition-and-conditionals-homework/src/main/java/edu/unl/cse/soft160/decomposition_and_conditionals/BooleanOperator.java b/decomposition-and-conditionals-homework/src/main/java/edu/unl/cse/soft160/decomposition_and_conditionals/BooleanOperator.java new file mode 100755 index 0000000000000000000000000000000000000000..4e0b5cf51d0f9238eb9ba3053a1f030ec1f345af --- /dev/null +++ b/decomposition-and-conditionals-homework/src/main/java/edu/unl/cse/soft160/decomposition_and_conditionals/BooleanOperator.java @@ -0,0 +1,11 @@ +package edu.unl.cse.soft160.decomposition_and_conditionals; + +public class BooleanOperator { + private static enum TruthValue { + TRUE, FALSE, UNKNOWN, + } + + public static void main(String... arguments) { + // [write code here] + } +} diff --git a/decomposition-and-conditionals-homework/src/main/java/edu/unl/cse/soft160/decomposition_and_conditionals/ProcessInput.java b/decomposition-and-conditionals-homework/src/main/java/edu/unl/cse/soft160/decomposition_and_conditionals/ProcessInput.java new file mode 100755 index 0000000000000000000000000000000000000000..42892798acc12a1cb1e14a81e4e1c3d065c8f363 --- /dev/null +++ b/decomposition-and-conditionals-homework/src/main/java/edu/unl/cse/soft160/decomposition_and_conditionals/ProcessInput.java @@ -0,0 +1,15 @@ +package edu.unl.cse.soft160.decomposition_and_conditionals; + +public class ProcessInput { + private static enum State { + EMPTY, DECIMAL, NUMERIC, + } + + private static enum Classification { + FLOAT, INTEGER, NAN, + } + + public static void main(String... arguments) { + // [write code here] + } +} diff --git a/decomposition-and-conditionals-homework/src/test/java/edu/unl/cse/soft160/decomposition_and_conditionals/BooleanOperatorTest.java b/decomposition-and-conditionals-homework/src/test/java/edu/unl/cse/soft160/decomposition_and_conditionals/BooleanOperatorTest.java new file mode 100644 index 0000000000000000000000000000000000000000..d8aadc8e6eba3f8f68db26165d5bbc0330ceb96e --- /dev/null +++ b/decomposition-and-conditionals-homework/src/test/java/edu/unl/cse/soft160/decomposition_and_conditionals/BooleanOperatorTest.java @@ -0,0 +1,107 @@ +package edu.unl.cse.soft160.decomposition_and_conditionals; + +import static org.junit.Assert.*; + +import org.junit.Test; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.io.PrintStream; + +public class BooleanOperatorTest { + + protected static String assemble(String... lines) { + return String.join("\n", lines) + "\n"; + } + + protected static String runMain(String... inputs) { + InputStream in = System.in; + PrintStream out = System.out; + System.setIn(new ByteArrayInputStream(assemble(inputs).getBytes())); + ByteArrayOutputStream collector = new ByteArrayOutputStream(); + System.setOut(new PrintStream(collector)); + BooleanOperator.main(); + System.setIn(in); + System.setOut(out); + return collector.toString(); + } + + @Test + public void testTrueTrue() { + assertEquals(assemble( + "Enter truth value of operand 1: Enter truth value of operand 2: The NOR value of TRUE and TRUE is FALSE."), + runMain("TRUE", "TRUE")); + } + + @Test + public void testTrueFalse() { + assertEquals(assemble( + "Enter truth value of operand 1: Enter truth value of operand 2: The NOR value of TRUE and FALSE is FALSE."), + runMain("TRUE", "FALSE")); + } + + @Test + public void testFalseTrue() { + assertEquals(assemble( + "Enter truth value of operand 1: Enter truth value of operand 2: The NOR value of FALSE and TRUE is FALSE."), + runMain("FALSE", "TRUE")); + } + + @Test + public void testFalseFalse() { + assertEquals(assemble( + "Enter truth value of operand 1: Enter truth value of operand 2: The NOR value of FALSE and FALSE is TRUE."), + runMain("FALSE", "FALSE")); + } + + @Test + public void testFalseUnknown() { + assertEquals(assemble( + "Enter truth value of operand 1: Enter truth value of operand 2: The NOR value of FALSE and UNKNOWN is UNKNOWN."), + runMain("FALSE", "UNKNOWN")); + } + + @Test + public void testUnknownFalse() { + assertEquals(assemble( + "Enter truth value of operand 1: Enter truth value of operand 2: The NOR value of UNKNOWN and FALSE is UNKNOWN."), + runMain("UNKNOWN", "FALSE")); + } + + @Test + public void testUnknownUnknown() { + assertEquals(assemble( + "Enter truth value of operand 1: Enter truth value of operand 2: The NOR value of UNKNOWN and UNKNOWN is UNKNOWN."), + runMain("UNKNOWN", "UNKNOWN")); + } + + @Test + public void testTrueUnknown() { + assertEquals(assemble( + "Enter truth value of operand 1: Enter truth value of operand 2: The NOR value of TRUE and UNKNOWN is FALSE."), + runMain("TRUE", "UNKNOWN")); + } + + @Test + public void testUnknownTrue() { + assertEquals(assemble( + "Enter truth value of operand 1: Enter truth value of operand 2: The NOR value of UNKNOWN and TRUE is FALSE."), + runMain("UNKNOWN", "TRUE")); + } + + @Test + public void testExceptionInOp1() { + assertEquals(assemble( + "Enter truth value of operand 1: Enter truth value of operand 2: Truth value must be one of TRUE, FALSE, or UNKNOWN."), + runMain("FOO", "TRUE")); + } + + @Test + public void testExceptionInOp2() { + assertEquals(assemble( + "Enter truth value of operand 1: Enter truth value of operand 2: Truth value must be one of TRUE, FALSE, or UNKNOWN."), + runMain("TRUE", "BAR")); + } + +} diff --git a/decomposition-and-conditionals-homework/src/test/java/edu/unl/cse/soft160/decomposition_and_conditionals/ProcessInputTest.java b/decomposition-and-conditionals-homework/src/test/java/edu/unl/cse/soft160/decomposition_and_conditionals/ProcessInputTest.java new file mode 100644 index 0000000000000000000000000000000000000000..443be183205bc414fa912917d54c0b19cc183697 --- /dev/null +++ b/decomposition-and-conditionals-homework/src/test/java/edu/unl/cse/soft160/decomposition_and_conditionals/ProcessInputTest.java @@ -0,0 +1,365 @@ +package edu.unl.cse.soft160.decomposition_and_conditionals; + +import static org.junit.Assert.*; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.io.PrintStream; + +import org.junit.Test; + +public class ProcessInputTest { + + protected static String assemble(String... lines) { + return String.join("\n", lines) + "\n"; + } + + protected static String runMain(String... inputs) { + InputStream in = System.in; + PrintStream out = System.out; + System.setIn(new ByteArrayInputStream(assemble(inputs).getBytes())); + ByteArrayOutputStream collector = new ByteArrayOutputStream(); + System.setOut(new PrintStream(collector)); + ProcessInput.main(); + System.setIn(in); + System.setOut(out); + return collector.toString(); + } + + @Test + public void testEmptyL() { + assertEquals( + assemble("Enter the current state: Enter the next character: " + + "Classification: NAN"), + runMain("empty","L")); + } + + @Test + public void testEmpty0() { + assertEquals( + assemble("Enter the current state: Enter the next character: " + + "Classification: INTEGER"), + runMain("empty","0")); + } + + @Test + public void testEmpty1() { + assertEquals( + assemble("Enter the current state: Enter the next character: " + + "Classification: INTEGER"), + runMain("empty","1")); + } + + @Test + public void testEmpty2() { + assertEquals( + assemble("Enter the current state: Enter the next character: " + + "Classification: INTEGER"), + runMain("empty","2")); + } + + @Test + public void testEmpty3() { + assertEquals( + assemble("Enter the current state: Enter the next character: " + + "Classification: INTEGER"), + runMain("empty","3")); + } + + @Test + public void testEmpty4() { + assertEquals( + assemble("Enter the current state: Enter the next character: " + + "Classification: INTEGER"), + runMain("empty","4")); + } + + @Test + public void testEmpty5() { + assertEquals( + assemble("Enter the current state: Enter the next character: " + + "Classification: INTEGER"), + runMain("empty","5")); + } + + @Test + public void testEmpty6() { + assertEquals( + assemble("Enter the current state: Enter the next character: " + + "Classification: INTEGER"), + runMain("empty","6")); + } + + @Test + public void testEmpty7() { + assertEquals( + assemble("Enter the current state: Enter the next character: " + + "Classification: INTEGER"), + runMain("empty","7")); + } + + @Test + public void testEmpty8() { + assertEquals( + assemble("Enter the current state: Enter the next character: " + + "Classification: INTEGER"), + runMain("empty","8")); + } + + @Test + public void testEmpty9() { + assertEquals( + assemble("Enter the current state: Enter the next character: " + + "Classification: INTEGER"), + runMain("empty","9")); + } + + @Test + public void testEmptyPeriod() { + assertEquals( + assemble("Enter the current state: Enter the next character: " + + "Classification: FLOAT"), + runMain("empty",".")); + } + + @Test + public void testEmptyF() { + assertEquals( + assemble("Enter the current state: Enter the next character: " + + "Classification: NAN"), + runMain("empty","f")); + } + + @Test + public void testEmptyD() { + assertEquals( + assemble("Enter the current state: Enter the next character: " + + "Classification: NAN"), + runMain("empty","d")); + } + + @Test + public void testDecimalL() { + assertEquals( + assemble("Enter the current state: Enter the next character: " + + "Classification: NAN"), + runMain("decimal","L")); + } + + @Test + public void testDecimal0() { + assertEquals( + assemble("Enter the current state: Enter the next character: " + + "Classification: FLOAT"), + runMain("decimal","0")); + } + + @Test + public void testDecimal1() { + assertEquals( + assemble("Enter the current state: Enter the next character: " + + "Classification: FLOAT"), + runMain("decimal","1")); + } + + @Test + public void testDecimal2() { + assertEquals( + assemble("Enter the current state: Enter the next character: " + + "Classification: FLOAT"), + runMain("decimal","2")); + } + + @Test + public void testDecimal3() { + assertEquals( + assemble("Enter the current state: Enter the next character: " + + "Classification: FLOAT"), + runMain("decimal","3")); + } + + @Test + public void testDecimal4() { + assertEquals( + assemble("Enter the current state: Enter the next character: " + + "Classification: FLOAT"), + runMain("decimal","4")); + } + + @Test + public void testDecimal5() { + assertEquals( + assemble("Enter the current state: Enter the next character: " + + "Classification: FLOAT"), + runMain("decimal","5")); + } + + @Test + public void testDecimal6() { + assertEquals( + assemble("Enter the current state: Enter the next character: " + + "Classification: FLOAT"), + runMain("decimal","6")); + } + + @Test + public void testDecimal7() { + assertEquals( + assemble("Enter the current state: Enter the next character: " + + "Classification: FLOAT"), + runMain("decimal","7")); + } + + @Test + public void testDecimal8() { + assertEquals( + assemble("Enter the current state: Enter the next character: " + + "Classification: FLOAT"), + runMain("decimal","8")); + } + + @Test + public void testDecimal9() { + assertEquals( + assemble("Enter the current state: Enter the next character: " + + "Classification: FLOAT"), + runMain("decimal","9")); + } + + @Test + public void testDecimalPeriod() { + assertEquals( + assemble("Enter the current state: Enter the next character: " + + "Classification: NAN"), + runMain("decimal",".")); + } + + @Test + public void testDecimalF() { + assertEquals( + assemble("Enter the current state: Enter the next character: " + + "Classification: FLOAT"), + runMain("decimal","f")); + } + + @Test + public void testDecimalD() { + assertEquals( + assemble("Enter the current state: Enter the next character: " + + "Classification: FLOAT"), + runMain("decimal","d")); + } + + @Test + public void testNumericL() { + assertEquals( + assemble("Enter the current state: Enter the next character: " + + "Classification: INTEGER"), + runMain("numeric","L")); + } + + @Test + public void testNumeric0() { + assertEquals( + assemble("Enter the current state: Enter the next character: " + + "Classification: INTEGER"), + runMain("numeric","0")); + } + + @Test + public void testNumeric1() { + assertEquals( + assemble("Enter the current state: Enter the next character: " + + "Classification: INTEGER"), + runMain("numeric","1")); + } + + @Test + public void testNumeric2() { + assertEquals( + assemble("Enter the current state: Enter the next character: " + + "Classification: INTEGER"), + runMain("numeric","2")); + } + + @Test + public void testNumeric3() { + assertEquals( + assemble("Enter the current state: Enter the next character: " + + "Classification: INTEGER"), + runMain("numeric","3")); + } + + @Test + public void testNumeric4() { + assertEquals( + assemble("Enter the current state: Enter the next character: " + + "Classification: INTEGER"), + runMain("numeric","4")); + } + + @Test + public void testNumeric5() { + assertEquals( + assemble("Enter the current state: Enter the next character: " + + "Classification: INTEGER"), + runMain("numeric","5")); + } + + @Test + public void testNumeric6() { + assertEquals( + assemble("Enter the current state: Enter the next character: " + + "Classification: INTEGER"), + runMain("numeric","6")); + } + + @Test + public void testNumeric7() { + assertEquals( + assemble("Enter the current state: Enter the next character: " + + "Classification: INTEGER"), + runMain("numeric","7")); + } + + @Test + public void testNumeric8() { + assertEquals( + assemble("Enter the current state: Enter the next character: " + + "Classification: INTEGER"), + runMain("numeric","8")); + } + + @Test + public void testNumeric9() { + assertEquals( + assemble("Enter the current state: Enter the next character: " + + "Classification: INTEGER"), + runMain("numeric","9")); + } + + @Test + public void testNumericPeriod() { + assertEquals( + assemble("Enter the current state: Enter the next character: " + + "Classification: FLOAT"), + runMain("numeric",".")); + } + + @Test + public void testNumericF() { + assertEquals( + assemble("Enter the current state: Enter the next character: " + + "Classification: FLOAT"), + runMain("numeric","f")); + } + + @Test + public void testNumericD() { + assertEquals( + assemble("Enter the current state: Enter the next character: " + + "Classification: FLOAT"), + runMain("numeric","d")); + } +} diff --git a/sort_three_pairs/.gitignore b/sort_three_pairs/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..f35f60a357424aa265b3a6c1933c26af537c57ad --- /dev/null +++ b/sort_three_pairs/.gitignore @@ -0,0 +1,62 @@ +# Mac file finder metadata +.DS_Store +# Windows file metadata +._* +# Thumbnail image caches +Thumbs.db +ethumbs.db +# MS Office temporary file +~* +# Emacs backup file +*~ + +# Common +[Bb]in/ +[Bb]uild/ +[Oo]bj/ +[Oo]ut/ +[Tt]mp/ +[Xx]86/ +[Ii][Aa]32/ +[Xx]64/ +[Xx]86_64/ +[Xx]86-64/ +[Aa]rm +[Aa]32 +[Tt]32 +[Aa]64 +*.tmp +*.bak +*.bk +*.swp + +# Java files +*.class +javadoc/ + +# Maven +target/ +pom.xml.tag +pom.xml.releaseBackup +pom.xml.versionsBackup +pom.xml.next +release.properties +dependency-reduced-pom.xml +buildNumber.properties +.mvn/timing.properties + +# JetBrains (IntelliJ IDEA, PyCharm, etc) files +.idea/ +cmake-build-*/ +*.iml +*.iws +*.ipr + +# Eclipse files +.settings/ +.project +.classpath +.buildpath +.loadpath +.factorypath +local.properties diff --git a/sort_three_pairs/pom.xml b/sort_three_pairs/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..0766594570da21d5d469f241f0345892e29077ae --- /dev/null +++ b/sort_three_pairs/pom.xml @@ -0,0 +1,36 @@ +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + <groupId>edu.unl.cse.soft160.sort_three_pairs</groupId> + <artifactId>sort_three_pairs</artifactId> + <packaging>jar</packaging> + <version>1.0-SNAPSHOT</version> + <name>sort_three_pairs</name> + <url>http://maven.apache.org</url> + + <properties> + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> + </properties> + + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-compiler-plugin</artifactId> + <configuration> + <source>8</source> + <target>8</target> + </configuration> + </plugin> + </plugins> + </build> + + <dependencies> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <version>4.13</version> + <scope>test</scope> + </dependency> + </dependencies> +</project> diff --git a/sort_three_pairs/src/main/java/edu/unl/cse/soft160/sort_three_pairs/SortThreePairs.java b/sort_three_pairs/src/main/java/edu/unl/cse/soft160/sort_three_pairs/SortThreePairs.java new file mode 100644 index 0000000000000000000000000000000000000000..8e56ae3588ab96dbed7c23a2daddc41b6c223c99 --- /dev/null +++ b/sort_three_pairs/src/main/java/edu/unl/cse/soft160/sort_three_pairs/SortThreePairs.java @@ -0,0 +1,77 @@ +package edu.unl.cse.soft160.sort_three_pairs; + +public class SortThreePairs { + public static double readArgument(int position, String... arguments) { + return Double.parseDouble(arguments[position]); + } + + public static String formatPair(double x, double y) { + return "(" + x + ", " + y + ")"; + } + + public static int getPositionOfMinimum(double x0, double y0, double x1, double y1, double x2, double y2) { + int result = 0; + double xmin = x0; + double ymin = y0; + if (x1 < xmin || x1 == xmin && y1 < ymin) { + result = result + 1; + xmin = x1; + ymin = y1; + } + if (x2 < xmin || x2 == xmin && y2 < ymin) { + result = result + 1; + xmin = x2; + ymin = y2; + } + return result; + } + + public static void main(String... arguments) { + if (arguments.length != 6) { + System.err.println("Sorts three points by their x coordinates, breaking ties using y coordinates."); + System.err.println( + "Usage: java edu.unl.cse.soft160.sort_three_pairs.SortThreePairs [X0] [Y0] [X1] [Y1] [X2] [Y2]"); + System.exit(1); + } + double x0 = readArgument(0, arguments); + double y0 = readArgument(1, arguments); + double x1 = readArgument(2, arguments); + double y1 = readArgument(3, arguments); + double x2 = readArgument(4, arguments); + double y2 = readArgument(5, arguments); + double x3 = x0; + double y3 = y0; + // move the smallest pair to (x0, y0) + int firstPositionOfMinimum = getPositionOfMinimum(x0, y0, x1, y1, x2, y2); + switch (firstPositionOfMinimum) { + case 2: + x3 = x1; + x1 = x2; + x2 = x3; + y3 = y1; + y1 = y2; + y2 = y3; + case 1: + x3 = x0; + x0 = x1; + x1 = x3; + y3 = y0; + y0 = y1; + y1 = y3; + } + // move the second-smallest pair to (x1, y1) + int secondPositionOfMinimum = getPositionOfMinimum(Double.MAX_VALUE, Double.MAX_VALUE, x1, y1, x2, y2); + switch (secondPositionOfMinimum) { + case 2: + x3 = x1; + x1 = x2; + x2 = x3; + y3 = y1; + y1 = y2; + y2 = y3; + } + System.out.println(formatPair(x1, y1)); + System.out.println(formatPair(x2, y2)); + System.out.println(formatPair(x3, y3)); + } +} diff --git a/sort_three_pairs/src/test/java/edu/unl/cse/soft160/sort_three_pairs/SortThreePairsTest.java b/sort_three_pairs/src/test/java/edu/unl/cse/soft160/sort_three_pairs/SortThreePairsTest.java new file mode 100644 index 0000000000000000000000000000000000000000..5c425e59dc97b1aa6d0b6e50e5e1bd57b9532b71 --- /dev/null +++ b/sort_three_pairs/src/test/java/edu/unl/cse/soft160/sort_three_pairs/SortThreePairsTest.java @@ -0,0 +1,167 @@ +package edu.unl.cse.soft160.sort_three_pairs; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import java.io.InputStream; +import java.io.ByteArrayInputStream; +import java.io.PrintStream; +import java.security.Permission; +import java.io.ByteArrayOutputStream; + +import static org.junit.Assert.*; + +public class SortThreePairsTest { + protected static String assemble(String... lines) { + return String.join("\n", lines) + "\n"; + } + + protected static String runMain(String... arguments) { + InputStream in = System.in; + PrintStream out = System.out; + System.setIn(new ByteArrayInputStream("".getBytes())); + ByteArrayOutputStream collector = new ByteArrayOutputStream(); + System.setOut(new PrintStream(collector)); + SortThreePairs.main(arguments); + System.setIn(in); + System.setOut(out); + return collector.toString(); + } + + @SuppressWarnings("serial") + protected static class ExitException extends SecurityException { + public final int status; + + public ExitException(int status) { + super("Exit with status " + status); + this.status = status; + } + } + + private static class TestingSecurityManager extends SecurityManager { + @Override + public void checkPermission(Permission perm) { + } + + @Override + public void checkPermission(Permission perm, Object context) { + } + + @Override + public void checkExit(int status) { + super.checkExit(status); + throw new ExitException(status); + } + } + + @Before + public void setUp() throws SecurityException { + System.setSecurityManager(new TestingSecurityManager()); + } + + @After + public void tearDown() throws SecurityException { + System.setSecurityManager(null); + } + + protected static String runMainForError(int expectedStatus, String... arguments) { + InputStream in = System.in; + PrintStream err = System.err; + System.setIn(new ByteArrayInputStream("".getBytes())); + ByteArrayOutputStream collector = new ByteArrayOutputStream(); + System.setErr(new PrintStream(collector)); + boolean exited = false; + try { + SortThreePairs.main(arguments); + } catch (ExitException expected) { + assertEquals(expectedStatus, expected.status); + exited = true; + } finally { + System.setIn(in); + System.setErr(err); + } + assertTrue(exited); + return collector.toString(); + } + + @Test + public void testZeros() { + assertEquals(assemble("(0.0, 0.0)", "(0.0, 0.0)", "(0.0, 0.0)"), + runMain("0", "0", "0", "0", "0", "0")); + } + + @Test + public void testAlreadySorted() { + assertEquals(assemble("(0.0, 0.0)", "(1.0, 1.0)", "(2.0, 2.0)"), + runMain("0", "0", "1", "1", "2", "2")); + } + + @Test + public void testReversed() { + assertEquals(assemble("(0.0, 0.0)", "(1.0, 1.0)", "(2.0, 2.0)"), + runMain("2", "2", "1", "1", "0", "0")); + } + + @Test + public void testJumbled() { + assertEquals(assemble("(0.0, 0.0)", "(1.0, 1.0)", "(2.0, 2.0)"), + runMain("1", "1", "2", "2", "0", "0")); + } + + @Test + public void testTieBreaking() { + assertEquals(assemble("(0.0, 1.0)", "(1.0, 2.0)", "(1.0, 3.0)"), + runMain("1", "3", "0", "1", "1", "2")); + } + + @Test + public void testMultipleTieBreaking() { + assertEquals(assemble("(0.0, 0.0)", "(0.0, 1.0)", "(0.0, 3.0)"), + runMain("0", "1", "0", "0", "0", "3")); + } + + @Test + public void testDuplicates() { + assertEquals(assemble("(1.0, 1.0)", "(1.0, 1.0)", "(1.0, 2.0)"), + runMain("1", "1", "1", "2", "1", "1")); + } + + @Test + public void testPermutation1() { + assertEquals(assemble("(0.0, 0.0)", "(1.0, 1.0)", "(2.0, 2.0)"), + runMain("0", "0", "2", "2", "1", "1")); + } + + @Test + public void testPermutation2() { + assertEquals(assemble("(0.0, 0.0)", "(1.0, 1.0)", "(2.0, 2.0)"), + runMain("2", "2", "0", "0", "1", "1")); + } + + @Test + public void testPermutation3() { + assertEquals(assemble("(0.0, 0.0)", "(1.0, 1.0)", "(2.0, 2.0)"), + runMain("2", "2", "1", "1", "0", "0")); + } + + @Test + public void testPermutation16() { + assertEquals(assemble("(0.0, 0.0)", "(1.0, 1.0)", "(2.0, 2.0)"), + runMain("1", "1", "2", "2", "0", "0")); + } + + @Test + public void testPermutation20() { + assertEquals(assemble("(0.0, 0.0)", "(1.0, 1.0)", "(2.0, 2.0)"), + runMain("1", "1", "0", "0", "2", "2")); + } + + @Test + public void testTooFewArguments() { + assertEquals( + assemble("Sorts three points by their x coordinates, breaking ties using y coordinates.", + "Usage: java edu.unl.cse.soft160.sort_three_pairs.SortThreePairs [X0] [Y0] [X1] [Y1] [X2] [Y2]"), + runMainForError(1, "0", "0", "1", "1", "2", "2", "3")); + } +} diff --git a/tetris/.gitignore b/tetris/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..f35f60a357424aa265b3a6c1933c26af537c57ad --- /dev/null +++ b/tetris/.gitignore @@ -0,0 +1,62 @@ +# Mac file finder metadata +.DS_Store +# Windows file metadata +._* +# Thumbnail image caches +Thumbs.db +ethumbs.db +# MS Office temporary file +~* +# Emacs backup file +*~ + +# Common +[Bb]in/ +[Bb]uild/ +[Oo]bj/ +[Oo]ut/ +[Tt]mp/ +[Xx]86/ +[Ii][Aa]32/ +[Xx]64/ +[Xx]86_64/ +[Xx]86-64/ +[Aa]rm +[Aa]32 +[Tt]32 +[Aa]64 +*.tmp +*.bak +*.bk +*.swp + +# Java files +*.class +javadoc/ + +# Maven +target/ +pom.xml.tag +pom.xml.releaseBackup +pom.xml.versionsBackup +pom.xml.next +release.properties +dependency-reduced-pom.xml +buildNumber.properties +.mvn/timing.properties + +# JetBrains (IntelliJ IDEA, PyCharm, etc) files +.idea/ +cmake-build-*/ +*.iml +*.iws +*.ipr + +# Eclipse files +.settings/ +.project +.classpath +.buildpath +.loadpath +.factorypath +local.properties diff --git a/tetris/logs/tetris.log b/tetris/logs/tetris.log new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/tetris/pom.xml b/tetris/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..47dd09f2e1121ba106b69cdd31adb89affab50de --- /dev/null +++ b/tetris/pom.xml @@ -0,0 +1,36 @@ +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + <groupId>edu.unl.cse.soft160.tetris</groupId> + <artifactId>tetris</artifactId> + <packaging>jar</packaging> + <version>1.0-SNAPSHOT</version> + <name>tetris</name> + <url>http://maven.apache.org</url> + + <properties> + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> + </properties> + + <build> + <plugins> + <plugin> + <artifactId>maven-compiler-plugin</artifactId> + <version>3.8.1</version> + <configuration> + <source>8</source> + <target>8</target> + </configuration> + </plugin> + </plugins> + </build> + + <dependencies> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <version>4.13</version> + <scope>test</scope> + </dependency> + </dependencies> +</project> diff --git a/tetris/src/main/java/edu/unl/cse/soft160/tetris/Tetris.java b/tetris/src/main/java/edu/unl/cse/soft160/tetris/Tetris.java new file mode 100644 index 0000000000000000000000000000000000000000..4190fb10183abb1a95f915cf9662b7e39134dfbe --- /dev/null +++ b/tetris/src/main/java/edu/unl/cse/soft160/tetris/Tetris.java @@ -0,0 +1,354 @@ +package edu.unl.cse.soft160.tetris; + +import java.util.Collections; +import java.util.ArrayList; + +import java.awt.Font; +import java.awt.Color; +import java.awt.Graphics; +import javax.swing.JFrame; +import javax.swing.JPanel; + +import java.awt.event.KeyEvent; +import java.awt.event.KeyListener; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.nio.file.StandardOpenOption; + +public class Tetris extends JFrame { + private static final long serialVersionUID = -6851893161385783635L; + + protected static void log(String message) { + try { + Files.write(Paths.get("logs/tetris.log"), (message + "\n").getBytes(), StandardOpenOption.CREATE, + StandardOpenOption.APPEND); + } catch (IOException exception) { + exception.printStackTrace(); + } + } + + protected void logFailedMove(String moveName) { + log("Attempted move failed: " + moveName + " on shape " + shape + " (\"" + SHAPE_NAMES[shape] + "\")"); + } + + protected static int mod(int value, int modulus) { + int result = value % modulus; + return result < 0 ? result + modulus : result; + } + + protected static final int DEFAULT_SQUARE_SIZE = 60; // pixels + + protected static final Font TEXT_FONT = new Font(Font.SANS_SERIF, Font.BOLD, DEFAULT_SQUARE_SIZE / 2); + protected static final Color TEXT_COLOR = new Color(255, 255, 255, 127); + + protected static final String ROWS_CLEARED = "Rows cleared"; + protected static final String FINAL_SCORE = "Final score"; + protected static final String SCORE_SEPARATOR = ": "; + + protected static final int ROTATE_LEFT = KeyEvent.VK_DOWN; + protected static final int ROTATE_RIGHT = KeyEvent.VK_UP; + protected static final int SHIFT_LEFT = KeyEvent.VK_LEFT; + protected static final int SHIFT_RIGHT = KeyEvent.VK_RIGHT; + protected static final int DROP = KeyEvent.VK_SPACE; + protected static final int PAUSE = KeyEvent.VK_P; + protected static final int NEW_GAME = KeyEvent.VK_N; + + protected static final int WIDTH = 7; // squares + protected static final int HEIGHT = 11; // squares + protected static final int START_Y = 1; // squares from top + + protected static final Color EMPTY = new Color(0, 0, 0); + protected static final Color[] COLORS = { new Color(0, 0, 204), new Color(142, 0, 204), new Color(204, 204, 0), + new Color(204, 0, 204), new Color(0, 204, 204), new Color(0, 204, 0), new Color(204, 0, 0), }; + + protected static final String[] SHAPE_NAMES = { "O", "L", "J", "S", "Z", "T", "I", }; + protected static final int[] SHAPE_WIDTHS = { 2, 3, 3, 3, 3, 3, 4, }; + protected static final int[][][][] SHAPES = { + { // O + { { 0, 0 }, { 1, 0 }, { 0, 1 }, { 1, 1 } }, }, + { // L + { { 0, 0 }, { 1, 0 }, { 2, 0 }, { 2, 1 } }, { { 1, -1 }, { 1, 0 }, { 1, 1 }, { 0, 1 } }, + { { 0, -1 }, { 0, 0 }, { 1, 0 }, { 2, 0 } }, { { 1, -1 }, { 2, -1 }, { 1, 0 }, { 1, 1 } }, }, + { // J + { { 0, 0 }, { 1, 0 }, { 2, 0 }, { 0, 1 } }, { { 0, -1 }, { 1, -1 }, { 1, 0 }, { 1, 1 } }, + { { 2, -1 }, { 0, 0 }, { 1, 0 }, { 2, 0 } }, { { 1, -1 }, { 1, 0 }, { 1, 1 }, { 2, 1 } }, }, + { // S + { { 0, 0 }, { 1, 0 }, { 1, 1 }, { 2, 1 } }, { { 1, 0 }, { 0, 1 }, { 1, 1 }, { 0, 2 } }, }, + { // Z + { { 1, 0 }, { 2, 0 }, { 0, 1 }, { 1, 1 } }, { { 0, 0 }, { 0, 1 }, { 1, 1 }, { 1, 2 } }, }, + { // T + { { 1, 0 }, { 0, 1 }, { 1, 1 }, { 2, 1 } }, { { 1, 0 }, { 1, 1 }, { 2, 1 }, { 1, 2 } }, + { { 0, 1 }, { 1, 1 }, { 2, 1 }, { 1, 2 } }, { { 1, 0 }, { 0, 1 }, { 1, 1 }, { 1, 2 } }, }, + { // I + { { 0, 0 }, { 1, 0 }, { 2, 0 }, { 3, 0 } }, { { 0, 0 }, { 0, 1 }, { 0, 2 }, { 0, 3 } }, }, }; + + protected Color[][] board; + protected int score; + + protected boolean isOccupied(int x, int y) { + return x < 0 || x >= WIDTH || y < 0 || y >= HEIGHT || board[x][y] != EMPTY; + } + + protected void emptyBoard() { + board = new Color[WIDTH][HEIGHT]; + for (int x = 0; x < WIDTH; ++x) { + for (int y = 0; y < HEIGHT; ++y) { + board[x][y] = EMPTY; + } + } + } + + protected void clearRows() { + int fallTo = HEIGHT - 1; + for (int y = HEIGHT; y-- > 0;) { + boolean full = true; + for (int x = 0; x < WIDTH; ++x) { + if (board[x][y] == EMPTY) { + full = false; + break; + } + } + if (full) { + ++score; + } else { + for (int x = 0; x < WIDTH; ++x) { + board[x][fallTo] = board[x][y]; + } + --fallTo; + } + } + for (int x = 0; x < WIDTH; ++x) { + for (int y = fallTo; y >= 0; --y) { + board[x][y] = EMPTY; + } + } + } + + protected long getDelay() { + return 20000 / (score + 50); + } + + protected ArrayList<Integer> currentShuffle = new ArrayList<Integer>(); + protected int currentShuffleIndex; + + protected int nextShape() { + if (currentShuffleIndex >= currentShuffle.size()) { + currentShuffleIndex = 0; + currentShuffle.clear(); + for (int i = 0; i < SHAPES.length; ++i) { + currentShuffle.add(i); + } + Collections.shuffle(currentShuffle); + } + return currentShuffle.get(currentShuffleIndex++); + } + + protected int shape; + protected int orientation; + protected int x; + protected int y; + + protected void createShape() { + shape = nextShape(); + orientation = 0; + x = (WIDTH - SHAPE_WIDTHS[shape]) / 2; + y = START_Y; + } + + protected int[][] getFallingSquares() { + int[][] result = { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }; + int[][][] entry = SHAPES[shape]; + int[][] offsets = entry[mod(orientation, entry.length)]; + for (int i = 0; i < 4; ++i) { + for (int j = 0; j < 2; ++j) { + result[i][j] = (j == 0 ? x : y) + offsets[i][j]; + } + } + return result; + } + + protected boolean shapeCollides() { + for (int[] square : getFallingSquares()) { + if (isOccupied(square[0], square[1])) { + return true; + } + } + return false; + } + + protected void drawShape(Color color) { + for (int[] square : getFallingSquares()) { + board[square[0]][square[1]] = color; + } + } + + protected void placeShape() { + drawShape(COLORS[shape]); + JPanel panel = (JPanel)getContentPane(); + panel.paintImmediately(0, 0, panel.getWidth(), panel.getHeight()); + } + + protected void unplaceShape() { + drawShape(EMPTY); + } + + protected boolean playing = false; + protected boolean paused = false; + + protected boolean isPlaying() { + return playing && !paused; + } + + protected void togglePause() { + paused = !paused; + } + + protected void beginGame() { + emptyBoard(); + createShape(); + placeShape(); + score = 0; + playing = true; + paused = false; + } + + protected void endGame() { + playing = false; + } + + protected boolean maybeAdjustShape(int rotation, int dx, int dy) { + if (!isPlaying()) { + return true; + } + unplaceShape(); + orientation += rotation; + x += dx; + y += dy; + boolean collides = shapeCollides(); + if (collides) { + orientation -= rotation; + x -= dx; + y -= dy; + } + placeShape(); + return collides; + } + + protected boolean sinkShape() { + if (!isPlaying()) { + return true; + } + if (maybeAdjustShape(0, 0, 1)) { + clearRows(); + createShape(); + if (shapeCollides()) { + endGame(); + } else { + placeShape(); + } + return true; + } + return false; + } + + protected void dropShape() { + while (!sinkShape()) + ; + } + + protected void rotateShapeLeft() { + maybeAdjustShape(-1, 0, 0); + } + + protected void rotateShapeRight() { + maybeAdjustShape(1, 0, 0); + } + + protected void shiftShapeLeft() { + maybeAdjustShape(0, -1, 0); + } + + protected void shiftShapeRight() { + maybeAdjustShape(0, 1, 0); + } + + public Tetris(int width, int height) { + super("Tetris"); + setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + setSize(width, height); + setContentPane(new JPanel() { + @Override + public void paint(Graphics graphics) { + super.paint(graphics); + int xScale = getWidth() / Tetris.WIDTH; + int yScale = getHeight() / Tetris.HEIGHT; + for (int x = 0; x < Tetris.WIDTH; ++x) { + for (int y = 0; y < Tetris.HEIGHT; ++y) { + graphics.setColor(board[x][y]); + graphics.fillRect(xScale * x, yScale * y, xScale, yScale); + } + } + String scoreString = (playing ? ROWS_CLEARED : FINAL_SCORE) + SCORE_SEPARATOR + String.valueOf(score); + graphics.setFont(TEXT_FONT); + graphics.setColor(TEXT_COLOR); + graphics.drawString(scoreString, (getWidth() - graphics.getFontMetrics().stringWidth(scoreString)) / 2, + (getHeight() / 2 - graphics.getFontMetrics().getHeight()) / 2); + } + }); + addKeyListener(new KeyListener() { + public void keyPressed(KeyEvent event) { + switch (event.getKeyCode()) { + case ROTATE_LEFT: + rotateShapeLeft(); + break; + case ROTATE_RIGHT: + rotateShapeRight(); + break; + case SHIFT_LEFT: + shiftShapeLeft(); + break; + case SHIFT_RIGHT: + shiftShapeRight(); + break; + case DROP: + dropShape(); + break; + case PAUSE: + togglePause(); + break; + case NEW_GAME: + if (playing) { + endGame(); + } + beginGame(); + break; + } + } + + public void keyReleased(KeyEvent e) { + } + + public void keyTyped(KeyEvent e) { + } + }); + new Thread() { + @Override + public void run() { + while (true) { + try { + Thread.sleep(getDelay()); + sinkShape(); + } catch (InterruptedException ignored) { + } + } + } + }.start(); + } + + public static void main(String... arguments) { + Tetris tetris = new Tetris(WIDTH * DEFAULT_SQUARE_SIZE, HEIGHT * DEFAULT_SQUARE_SIZE); + tetris.setVisible(true); + tetris.beginGame(); + } +}